#!/usr/bin/env bash

WORKDIR="/opt/valet-linux"
DNSFILE="${WORKDIR}/dns-servers"
DNSHEAD="${WORKDIR}/custom-nameservers"
LOGFILE="${WORKDIR}/watch.log"
VRESOLV="${WORKDIR}/resolv.conf"
RESOLV="/etc/resolv.conf"

function unique() {
    # Function to remove duplicated lines (even when they are not contiguous)
    # cat -n      puts line numbers
    # sort -uk2   sort and remove duplicates (ignoring line numbers)
    # sort -nk1   sort by line number
    # cut -f2-    remove line numbers
    cat -n | sort -uk2 | sort -nk1 | cut -f2-
}

function symlinkResolv() {
    if [[ $(readlink -f "$RESOLV") != "$VRESOLV" ]]; then
        if [[ ! -f "${RESOLV}.bak" ]]; then
            mv "$RESOLV" "${RESOLV}.bak"
        fi

        ln -sf "$VRESOLV" "${RESOLV}.tmp"
        mv "${RESOLV}.tmp" "$RESOLV"
    fi
}

function getDirs() {
    DIRS=()
    local TARRAY=()

    readarray -t TARRAY <<< "$(find /run -path '/run/user' -prune -o -path '/run/media' -prune -o ! -readable -prune -o -name 'resolv.conf' -print)"

    # Find nameserver files in the /run/NetworkManager folder (as they do not have a standard name)
    if [[ ! -f "/run/NetworkManager/resolv.conf" && -d "/run/NetworkManager" ]]; then
        TARRAY=(${TARRAY[@]} '/run/NetworkManager/')
    fi

    # Find nameserver files in the /run/resolvconf/interface folder (as they do not have a standard name)
    if [[ -d "/run/resolvconf/interface" ]]; then
        TARRAY=(${TARRAY[@]} '/run/resolvconf/interface/')
    fi

    for ENTRY in "${TARRAY[@]}"; do
        local TMP=${ENTRY%/*}
        DIRS=(${DIRS[@]} "$TMP")
    done
}

function updateNameservers() {
    # Read all of the nameserver files at once, filter lines starting with 'nameserver'
    # and exclude the ones containing 127.0.0.1 (localhost)

    getFiles

    echo "${FILES[@]}" | tee "${WORKDIR}/resolvfiles.log" &>/dev/null

    cat "$DNSHEAD" | unique | tee "$DNSFILE" &>/dev/null
    cat "${FILES[@]}" | grep -i '^nameserver' | grep -vE '127\.(0\.){2}\d{1,3}' | unique | tee -a "$DNSFILE" &>/dev/null

    symlinkResolv

    cat "${FILES[@]}" | grep -v '^nameserver' | grep -v '^#' | unique | tee "$VRESOLV" &>/dev/null
    echo 'nameserver 127.0.0.1' >> "$VRESOLV"

    # Add "search" and "domain" directives to /etc/resolv.conf
    # chattr -i "$RESOLV" && \
    # cat "${FILES[@]}" | grep -v '^nameserver' | grep -v '^#' | unique | tee "$VRESOLV" &>/dev/null && \
    # echo 'nameserver 127.0.0.1' >> "$VRESOLV" && \
    # chattr +i "$RESOLV"
}

function getFiles() {
    FILES=()
    local TARRAY=()

    for DIR in "${DIRS[@]}"; do
        readarray -t TARRAY <<< "$(find ${DIR} -path ! -readable -prune -o -name 'resolv.conf' -print)"

        # Find nameserver files in the /run/resolvconf/interface folder (as they do not have a standard name)
        if [[ "$DIR" = "/run/resolvconf/interface" ]]; then
            readarray -t TARRAY <<< "$(find ${DIR} ! -readable -prune -o -type f -print)"
        fi

        FILES=(${FILES[@]} ${TARRAY[@]})
    done
}

function watchDirs() {
    local WATCHERS=(${DIRS[@]} "$DNSHEAD")

    # Log which directories are being watched
    echo "Watching the following directories for changes:" >> "$LOGFILE"

    for DIR in "${DIRS[@]}"; do
        echo " - $DIR" >> "$LOGFILE"
    done

    # Watch directories for changes in files
    inotifywait -q -m -e modify -e create -e delete --format "%w%f" "${WATCHERS[@]}" | while read change; do
        updateNameservers
    done &
}

function main() {
    # Create dns file in case it does not exists
    if [[ ! -f "$DNSHEAD" ]]
    then
        echo "Creating default $DNSHEAD file"
        echo nameserver 1.1.1.1 > $DNSHEAD
    fi

    touch "$DNSFILE"

    # Clear log file
    if [[ -f "$LOGFILE" ]]; then
        rm "$LOGFILE"
    fi

    touch "$LOGFILE"

    getDirs
    updateNameservers
    watchDirs
}

################################################################################

function start {
    if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then
        echo -e "Valet DNS Watcher is already running..."
    else
        echo -e "Starting Valet DNS Watcher..."
        main
        sleep 2 && echo $(pgrep -f 'inotifywait -q -m -e modify') > "${WORKDIR}/watch.pid"
        echo -e "Valet DNS Watcher started succesfully."
    fi
}

function stop {
    echo -e "Stopping Valet DNS Watcher...\n"

    pkill -f "inotifywait -q -m -e modify"

    rm "$LOGFILE" && touch "$LOGFILE"

    if [[ ! $(pgrep -f 'inotifywait -q -m -e modify') ]]; then
        echo -e "\nValet DNS Watcher stopped succesfully."
    fi
}

function restart {
    echo -e "Stopping Valet DNS Watcher..."

    if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then
        pkill -f "inotifywait -q -m -e modify"
    fi

    echo -e "Starting Valet DNS Watcher..."

    main

    if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then
        echo -e "Valet DNS Watcher restarted succesfully."
    fi
}

function status {
    if [[ -f "$LOGFILE" ]]; then
        echo -e "Valet DNS service is running correctly.\n"
        cat '/opt/valet-linux/watch.log'
    else
        echo "Valet DNS service is not running."
    fi
}

case "$1" in
   start)
      start
   ;;
   stop)
      stop
   ;;
   restart)
      restart
   ;;
   status)
      status
   ;;
   *)
      echo "Usage: $0 {start|stop|restart|status}"
esac

exit 0
